// i.d(t, { v: () => ObservableMap, q: () => o })
import Observable from "./observable"
import funcUtil from "../utils/func.util"

function numToStr(e) {
  return funcUtil.isNumber(e) ? "" + e : e
}
export default class ObservableMap<T = any> extends Observable {
  protected _keysSet: Set<string>
  protected _keysList: string[]
  map: Record<string, T>
  constructor() {
    super()
    this.map = {}
    this._keysSet = new Set()
    this._keysList = []
  }
  get keys() {
    return this._keysList.slice()
  }
  get values() {
    return this._keysList.map(e => this.get(e))
  }
  entries() {
    const e = []
    for (const t in this.map) e.push([t, this.map[t]])
    return e
  }
  get length() {
    return this._keysList.length
  }
  *[Symbol.iterator]() {
    for (const e of this._keysList) yield this.get(e)
  }
  get(e: string) {
    return this.map[e]
  }
  has(e: string) {
    return void 0 !== this.map[e]
  }
  set(e: string, t: T) {
    e = numToStr(e)
    this.map[e] !== t &&
      (this.removeChildObservables(this.map[e]),
      this.addChildObservable(t),
      (this.map[e] = t),
      this._keysSet.has(e) || (this._keysList.push(e), this._keysSet.add(e)),
      this.setDirty())
  }
  clear() {
    if (this.length > 0) {
      for (const e in this.map) this.removeChildObservables(this.map[e])
      ;(this.map = {}), this._keysSet.clear(), (this._keysList = []), this.setDirty()
    }
  }
  delete(e) {
    ;(e = numToStr(e)), this.removeChildObservables(this.map[e])
    const t = this.map.hasOwnProperty(e)
    return delete this.map[e], t && (this._keysSet.delete(e), this._keysList.splice(this._keysList.indexOf(e), 1), this.setDirty()), t
  }
  onElementChanged(e) {
    let t = Object.assign({}, this.map)
    return this.onChanged(() => {
      for (const i in t) e.onRemoved && !this.has(i) && e.onRemoved(t[i], i)
      for (const i of this._keysList) {
        const s = this.get(i)
        void 0 === t[i]
          ? e.onAdded && e.onAdded(s, i)
          : ((Observable.isObservable(s) && s["_dirtyObservable"]) || s !== t[i]) &&
            (e.onUpdated && null !== s ? e.onUpdated(s, i) : e.onRemoved && null === s && e.onRemoved(t[i], i))
      }
      t = Object.assign({}, this.map)
    })
  }
  deepCopy() {
    const e = {}
    for (const t of this._keysList) e[t] = funcUtil.deepCopy(this.map[t])
    return e
  }
}
export const createMap = (e = {}) => {
  const t = new ObservableMap()
  for (const i in e) t.set(i, e[i])
  return t
}
